package cn.com.cifi.mars.web.action.report;

import java.io.IOException;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;

import cn.com.cifi.mars.bean.bo.DivisionTreeBO;
import cn.com.cifi.mars.bean.dto.DivisionDto;
import cn.com.cifi.mars.bean.dto.RemovalReportDataDto;
import cn.com.cifi.mars.bean.vo.price.RemovalReportVO;
import cn.com.cifi.mars.bean.vo.price.req.RemovalReportReqParam;
import cn.com.cifi.mars.common.base.BaseAction;
import cn.com.cifi.mars.common.bean.R;
import cn.com.cifi.mars.common.constants.VersionConstant;
import cn.com.cifi.mars.common.enums.LocalCommandEnum;
import cn.com.cifi.mars.common.exception.MsgException;
import cn.com.cifi.mars.constant.CodeEnum;
import cn.com.cifi.mars.constant.Constants;
import cn.com.cifi.mars.constant.OrgTypeEunm;
import cn.com.cifi.mars.entity.DmDmRemovalReport;
import cn.com.cifi.mars.entity.DmDmValueReport;
import cn.com.cifi.mars.entity.SysProduct;
import cn.com.cifi.mars.entity.common.SysDictionary;
import cn.com.cifi.mars.service.DmDmRemovalReportService;
import cn.com.cifi.mars.service.DmDmValueReportService;
import cn.com.cifi.mars.service.IdmUserService;
import cn.com.cifi.mars.service.OrgService;
import cn.com.cifi.mars.service.SysDictionaryService;
import cn.com.cifi.mars.service.SysProductService;
import cn.com.cifi.mars.util.BeanConvertUtils;
import cn.com.cifi.mars.util.DateUtils;
import cn.com.cifi.mars.util.FileUtils;
import cn.com.cifi.mars.util.RemovalReportTemplate;
import cn.com.cifi.mars.util.RemovalReportUtils;
import cn.com.cifi.mars.util.ReportUtils;
import cn.com.cifi.mars.util.StrUtils;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;

/**
 * @author Administrator
 */
@Slf4j
@RestController
public class RemovalReportController extends BaseAction implements IRemovalReportController {

    private static final String REPORT_NAME_PATTERN = "去化率分析表-截止%s-%s";

    private static final String SHEET_NAME_PATTERN = "去化率分析表-截止%s";

    @Autowired
    private IdmUserService idmUserService;
    
    @Autowired
    private OrgService orgService;
    
    @Autowired
    private SysDictionaryService sysDictionaryService;

    @Autowired
    private DmDmValueReportService dmDmValueReportService;

    @Autowired
    private DmDmRemovalReportService dmDmRemovalReportService;
    
    @Autowired
    SysProductService sysProductService;
    
    private transient volatile boolean SYNC_BLOCK = false;

    private final transient Map<String,Object> LOCAL_CACHE = new HashMap<>();

    @PostConstruct
    private void init() {
        new Thread(() -> {
            try {
                if (dmDmRemovalReportService != null) {
                    LocalCommandEnum localCommandEnum;
                    while ((localCommandEnum = dmDmRemovalReportService.takeCommand()) != null) {
                        if (SYNC_BLOCK){
                            log.info("已有指令在执行，本次执行忽略：{}", localCommandEnum.getName());
                            continue;
                        }
                        SYNC_BLOCK = true;
                        try {
                            log.info("接收指令：{}", localCommandEnum.getName());
                            switch (localCommandEnum) {
                                case FLUSH_REMOVAL_REPORT_CACHE:
                                    flushBaseCache(LocalDate.now().format(DateTimeFormatter.ofPattern(DateUtils.DATE_JFP_STR)));
                                    break;
                                case CLEAR_REMOVAL_REPORT_CACHE:
                                    LOCAL_CACHE.clear();
                                    break;
                            }
                            log.info("指令：{},执行完毕", localCommandEnum.getName());
                        } catch (Exception e) {
                            log.error(e.getMessage(), e);
                        }finally {
                            SYNC_BLOCK = false;
                        }
                    }
                }
            } catch (InterruptedException e) {
                log.error(e.getMessage(), e);
                Thread.currentThread().interrupt();
            }
        }).start();
    }

    /**
     * 查询去化率报表数据
     *
     * @param param
     * @return
     */
    @Override
    public R<List<RemovalReportVO>> removalReport(
            @ApiParam(value = "接口版本", required = true, defaultValue = VersionConstant.V1) @PathVariable(value = "apiVersion") String apiVersion,
            RemovalReportReqParam param) {
        //查数据权限
        List<String> projectIdList = idmUserService.queryUserProjectPermissions(getOperator().getUsername());
        if (CollectionUtils.isEmpty(projectIdList)) {
            return ok(new ArrayList<>(0));
        }

        String localEndDate = LocalDate.now().format(DateTimeFormatter.ofPattern(DateUtils.DATE_JFP_STR));
        String endDate = param.getEndDate() == null ? localEndDate : param.getEndDate();
        Map<String, String[]> parentIdMap = new HashMap<>();
        Map<String, Map<String, RemovalReportVO>> buildingVOMap = null;

        //当前年月先查缓存
        if (localEndDate.equals(endDate)) {
            try {
                Object[] objects = makeReportFromCache(projectIdList, endDate, parentIdMap, 0);
                if (objects != null){
                    buildingVOMap = (Map<String, Map<String, RemovalReportVO>>) objects[1];
                }
            } catch (Exception e) {
                log.error(e.getMessage(),e);
            }
        }

        //历史年月或无缓存则计算
        if (buildingVOMap == null){
            //根据项目ID与截止年月查询数据
            String latestEndDate = dmDmValueReportService.getLatestEndDate(endDate);
            QueryWrapper<DmDmValueReport> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("end_date", latestEndDate).in("project_id", projectIdList);
            List<DmDmValueReport> dmDmValueReportList = dmDmValueReportService.list(queryWrapper);
            if (CollectionUtils.isEmpty(dmDmValueReportList)) {
                return ok(dmDmValueReportList);
            }

            //生成ID 父ID map
            RemovalReportUtils.makeOnlineReportParentIds(parentIdMap, dmDmValueReportList);

            //查询基础楼栋层级信息
            buildingVOMap = makeBaseBuildingVOMap(endDate, dmDmValueReportList, parentIdMap, projectIdList);
        }

        //楼栋到分期
        Map<String, Map<String, RemovalReportVO>> projectfMap = new HashMap<>();
        RemovalReportUtils.calcParent(parentIdMap, buildingVOMap, projectfMap);

        //分期到项目
        Map<String, Map<String, RemovalReportVO>> projectMap = new HashMap<>();
        RemovalReportUtils.calcParent(parentIdMap, projectfMap, projectMap);

        //项目到城市
        Map<String, Map<String, RemovalReportVO>> cityMap = new HashMap<>();
        RemovalReportUtils.calcParent(parentIdMap, projectMap, cityMap);

        //城市到集团
        Map<String, Map<String, RemovalReportVO>> regionMap = new HashMap<>();
        RemovalReportUtils.calcParent(parentIdMap, cityMap, regionMap);

        //集团到全部
        Map<String, Map<String, RemovalReportVO>> allMap = new HashMap<>();
        RemovalReportUtils.calcParent(parentIdMap, regionMap, allMap);


        //计算均价、总价、去化率
        List<RemovalReportVO> list = RemovalReportUtils.makeRemoval(endDate, projectfMap, projectMap, cityMap, regionMap, allMap);

        //单位处理
        RemovalReportUtils.makeRemovalUnit(list);

        //排序
        List<RemovalReportVO> sortedList = list.stream().sorted((a, b) -> {
            String aOrgType = StrUtils.nvl(a.getOrgType());
            String bOrgType = StrUtils.nvl(b.getOrgType());
            if (!aOrgType.equals(bOrgType)) {
                return aOrgType.compareTo(bOrgType);
            }
            String aName = StrUtils.nvl(a.getOrgName());
            String bName = StrUtils.nvl(b.getOrgName());
            return ReportUtils.compareStrStartedChinaNum(aName, bName);
        }).collect(Collectors.toList());
        return ok(sortedList);
    }

    private Object[] makeReportFromCache(List<String> projectIdList, String endDate, Map<String, String[]> parentIdMap,int type) {
        Object[] result = (Object[]) LOCAL_CACHE.get("removal:base:" + endDate);
        if (result == null) {
            return null;
        }

        List<DmDmValueReport> dmDmValueReportList = (List<DmDmValueReport>) result[0];
        Map<String, Map<String, RemovalReportVO>> buildingMap = (Map<String, Map<String, RemovalReportVO>>) result[1];

        //list to set 便于高效查询
        Set<String> projectIdSet = projectIdList.parallelStream().collect(Collectors.toSet());

        //过滤符合用户权限的数据
        List<DmDmValueReport> filteredDmDmValueReport = dmDmValueReportList.parallelStream().filter(x -> projectIdSet.contains(x.getProjectId())).collect(Collectors.toList());

        //创建父ID数据
        if (type == 0){
            //0为在线
            RemovalReportUtils.makeOnlineReportParentIds(parentIdMap, filteredDmDmValueReport);
        }else{
            //其他为导出
            RemovalReportUtils.makeExportReportParentIds(parentIdMap,dmDmValueReportList);
        }

        //取出用户符合权限的所有楼栋ID
        Set<String> buildingIdSet = filteredDmDmValueReport.parallelStream().map(x -> x.getBuildingId()).collect(Collectors.toSet());

        //从基础数据中过滤出符合用户数据的分组对象数据
        Map<String, Map<String, RemovalReportVO>> newBuildingVOMap = buildingMap.entrySet().parallelStream().filter(x -> buildingIdSet.contains(x.getKey())).collect(Collectors.toMap(x -> x.getKey(), x -> x.getValue(), (o, n) -> n));

        if (type == 0){
            //在线接口->基础数据楼栋无默认父级ID，此处将楼栋的默认父ID改为分期ID
            Map<String, String> buildingParentMap = filteredDmDmValueReport.parallelStream().map(x -> new String[]{x.getBuildingId(), x.getProjectfId()}).collect(Collectors.toMap(x -> x[0], x -> x[1], (o, n) -> n));
            newBuildingVOMap.values().parallelStream().flatMap(x -> x.values().parallelStream()).forEach(x -> {
                x.setParentOrgId(buildingParentMap.get(x.getOrgId()));
            });
        }else{
            //导出接口->基础数据楼栋无默认父级ID，此处将楼栋的默认父ID改为产品构成
            Map<String, String> buildingParentMap = filteredDmDmValueReport.parallelStream().map(x -> new String[]{x.getBuildingId(),  DigestUtils.md5Hex(x.getProjectfId() + x.getGroupCode() + x.getProductTypeCode() + x.getProductCode())}).collect(Collectors.toMap(x -> x[0], x -> x[1], (o, n) -> n));
            newBuildingVOMap.values().parallelStream().flatMap(x -> x.values().parallelStream()).forEach(x -> {
                x.setParentOrgId(buildingParentMap.get(x.getOrgId()));
            });
        }


        return new Object[]{filteredDmDmValueReport,newBuildingVOMap};
    }
    
    @Override
	public R exportCheck(String apiVersion, RemovalReportReqParam param) {
    	 String orgIds = param.getOrgIds();
         if(StrUtils.isEmpty(orgIds)) {
         	throw new MsgException(CodeEnum.NOT_SELECT_EXIST);
         }
         List<String> divisionList = Arrays.asList(orgIds.split(","));
         List<SysDictionary> sysDictionaryList = sysDictionaryService.selectListByTypeCode(Constants.REMOVAL_REPORT_EXPORT);
         String maxNum = getMaxNum(sysDictionaryList,Constants.REMOVAL_REPORT_EXPORT_MAX_NUM);
         if(StrUtils.isNotEmpty(maxNum) && divisionList.size() > Integer.parseInt(maxNum)) {
         	throw new MsgException(CodeEnum.MORE_SELECT_EXIST);
         }
         
         return ok();
	}
    
    @Override
    public void removalReportExport(String apiVersion, RemovalReportReqParam param, HttpServletResponse response) {
    	 log.info("###############去化率表导出   Start: "+ LocalDateTime.now());
    	 String orgIds = param.getOrgIds();
    	 List<String> divisionList = Arrays.asList(orgIds.split(","));
    	 String localEndDate = LocalDate.now().format(DateTimeFormatter.ofPattern(DateUtils.DATE_JFP_STR));
         String endDate = param.getEndDate() == null ? localEndDate : param.getEndDate();
         //截止年月最新时间
         String latestEndDate = dmDmValueReportService.getLatestEndDate(endDate);
         //查数据权限
         List<String> projectIdList = idmUserService.queryUserProjectPermissions(getOperator().getUsername());
         //事业部下有权限的项目
         List<String> projectIds = dmDmRemovalReportService.getProjectIds(divisionList,projectIdList);
         
         List<RemovalReportDataDto> dataList = dmDmRemovalReportService.getRemovalReportDataList(latestEndDate,projectIds,projectIdList);
         RemovalReportUtils.makeRemoval(dataList);
         //业态大类
         List<SysProduct> projectList = sysProductService.selectProductParent();
         //填充表格
         String endDateNoneSplit = endDate.replace("-", "");
         //报表名称
         String reportName = FileUtils.complianceFileName(String.format(REPORT_NAME_PATTERN, endDateNoneSplit, LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHH"))));
         //sheet名称
         String sheetName = FileUtils.complianceFileName(String.format(SHEET_NAME_PATTERN, endDateNoneSplit));
         long aStart = System.currentTimeMillis();
         RemovalReportTemplate template = new RemovalReportTemplate(dataList,projectList,endDate,sheetName,reportName,response);
         long aEnd = System.currentTimeMillis();
         log.info("###############填充表格数据耗时   : "+ (aEnd-aStart));
         try (OutputStream outputStream = response.getOutputStream()){
         	response.setContentType("application/msexcel");
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(reportName + ".xlsx", "utf-8"));
         	template.write(outputStream);
            outputStream.flush();
         } catch (IOException e) {
             log.error("###### errorMsg: ", e);
         }
    	 log.info("###############去化率表导出   End: "+ LocalDateTime.now());
    }

	private String getMaxNum(List<SysDictionary> sysDictionaryList,String code) {
		if(CollectionUtils.isNotEmpty(sysDictionaryList)) {
			for(SysDictionary obj : sysDictionaryList) {
				if(obj.getDictCode().equals(code)) {
					return obj.getDictValue();
				}
			}
		}
		return "";
	}
	
    private Map<String, Map<String, RemovalReportVO>> makeBaseBuildingVOMap(String endDate, List<DmDmValueReport> dmDmValueReportList, Map<String, String[]> parentIdMap, List<String> projectIdList) {
        Map<String, Map<String, RemovalReportVO>> buildingVOMap = new HashMap<>();
        //面积段货值拆分
        List<DmDmValueReport> areaSectionList = dmDmValueReportList.stream().filter(x -> StringUtils.isNotBlank(x.getMiniGranularityName())).collect(Collectors.toList());
        if (CollectionUtils.isNotEmpty(areaSectionList)) {
            Map<String, Map<String, RemovalReportVO>> areaSectionMap = new HashMap<>();
            areaSectionList.stream()
                    .map(areaSection -> BeanConvertUtils.toRemovalReportVOMap(areaSection, areaSection.getMiniGranularityId(), areaSection.getMiniGranularityName(), areaSection.getBuildingId(), OrgTypeEunm.ORG_HOUSE_PACKAGE.getKey()))
                    .flatMap(map -> map.entrySet().stream())
                    .forEach(entry -> {
                        areaSectionMap.put(entry.getKey(), entry.getValue());
                    });
            //面积段汇总至楼栋
            RemovalReportUtils.calcParent(parentIdMap, areaSectionMap, buildingVOMap);
        }

        //楼栋货值拆分、与面积段汇总的楼栋合并
        List<DmDmValueReport> buildingList = dmDmValueReportList.stream().filter(x -> StringUtils.isNotBlank(x.getBuildingId()) && StringUtils.isBlank(x.getMiniGranularityName())).collect(Collectors.toList());
        if (CollectionUtils.isNotEmpty(buildingList)) {
            buildingList.stream().map(building -> {
                String[] parent = parentIdMap.get(building.getBuildingId());
                return BeanConvertUtils.toRemovalReportVOMap(building, building.getBuildingId(), building.getBuildingName(), parent[0], OrgTypeEunm.ORG_BUILD.getKey());
            }).flatMap(x -> x.entrySet().stream()).forEach(entry -> {
                buildingVOMap.put(entry.getKey(), entry.getValue());
            });
        }

        //查楼栋供货、签约信息
        String lastEndDate = dmDmRemovalReportService.getLatestEndDate(endDate);
        QueryWrapper<DmDmRemovalReport> queryWrapper = new QueryWrapper();
        queryWrapper.eq("end_date", lastEndDate).in("project_id", projectIdList);
        List<DmDmRemovalReport> removalReports = dmDmRemovalReportService.list(queryWrapper);
        Map<String, DmDmRemovalReport> removalReportMap = removalReports.stream().collect(Collectors.toMap(x -> x.getBuildingId(), x -> x, (o, n) -> n));
        buildingVOMap.entrySet().stream().forEach(entry -> {
            DmDmRemovalReport removalReport = removalReportMap.get(entry.getKey());
            if (removalReport == null) {
                removalReport = new DmDmRemovalReport();
            }
            DmDmRemovalReport finalRemovalReport = removalReport;
            entry.getValue().values().stream().findFirst().ifPresent(demoVO -> {
                BeanConvertUtils
                        .toRemovalReportVOList(demoVO, finalRemovalReport, endDate)
                        .stream()
                        .forEach(x -> {
                            entry.getValue().put(x.getGroupCode(), x);
                        });
            });
        });
        return buildingVOMap;
    }

    private void flushBaseCache(String endDate) {
        //根据项目ID与截止年月查询数据
        String latestEndDate = dmDmValueReportService.getLatestEndDate(endDate);
        QueryWrapper<DmDmValueReport> dmDmValueReportQueryWrapper = new QueryWrapper<>();
        dmDmValueReportQueryWrapper.eq("end_date", latestEndDate);
        List<DmDmValueReport> dmDmValueReportList = dmDmValueReportService.list(dmDmValueReportQueryWrapper);
        if (CollectionUtils.isEmpty(dmDmValueReportList)) {
            return;
        }

        Map<String, Map<String, RemovalReportVO>> buildingVOMap = new HashMap<>();

        Map<String, String[]> parentIdMap = new HashMap<>();
        dmDmValueReportList.stream().forEach(dmDmValueReport -> {
            if (StringUtils.isNotBlank(dmDmValueReport.getMiniGranularityId()) && !parentIdMap.containsKey(dmDmValueReport.getMiniGranularityId())) {
                parentIdMap.put(dmDmValueReport.getMiniGranularityId(), new String[]{dmDmValueReport.getBuildingId(), dmDmValueReport.getBuildingName(), OrgTypeEunm.ORG_BUILD.getKey()});
            }
            if (!parentIdMap.containsKey(dmDmValueReport.getBuildingId())){
                parentIdMap.put(dmDmValueReport.getBuildingId(),new String[]{"","",""});
            }
        });

        //面积段货值拆分
        List<DmDmValueReport> areaSectionList = dmDmValueReportList.stream().filter(x -> StringUtils.isNotBlank(x.getMiniGranularityName())).collect(Collectors.toList());
        if (CollectionUtils.isNotEmpty(areaSectionList)) {
            Map<String, Map<String, RemovalReportVO>> areaSectionMap = new HashMap<>();
            areaSectionList.stream()
                    .map(areaSection -> BeanConvertUtils.toRemovalReportVOMap(areaSection, areaSection.getMiniGranularityId(), areaSection.getMiniGranularityName(), areaSection.getBuildingId(), OrgTypeEunm.ORG_HOUSE_PACKAGE.getKey()))
                    .flatMap(map -> map.entrySet().stream())
                    .forEach(entry -> {
                        areaSectionMap.put(entry.getKey(), entry.getValue());
                    });
            //面积段汇总至楼栋
            RemovalReportUtils.calcParent(parentIdMap, areaSectionMap, buildingVOMap);
        }

        //楼栋货值拆分、与面积段汇总的楼栋合并
        List<DmDmValueReport> buildingList = dmDmValueReportList.stream().filter(x -> StringUtils.isNotBlank(x.getBuildingId()) && StringUtils.isBlank(x.getMiniGranularityName())).collect(Collectors.toList());
        if (CollectionUtils.isNotEmpty(buildingList)) {
            buildingList.stream().map(building -> BeanConvertUtils.toRemovalReportVOMap(building, building.getBuildingId(), building.getBuildingName(), "", OrgTypeEunm.ORG_BUILD.getKey())).flatMap(x -> x.entrySet().stream()).forEach(entry -> {
                buildingVOMap.put(entry.getKey(), entry.getValue());
            });
        }

        //查楼栋供货、签约信息
        String lastEndDate = dmDmRemovalReportService.getLatestEndDate(endDate);
        QueryWrapper<DmDmRemovalReport> queryWrapper = new QueryWrapper();
        queryWrapper.eq("end_date", lastEndDate);
        List<DmDmRemovalReport> removalReports = dmDmRemovalReportService.list(queryWrapper);
        Map<String, DmDmRemovalReport> removalReportMap = removalReports.stream().collect(Collectors.toMap(x -> x.getBuildingId(), x -> x, (o, n) -> n));
        buildingVOMap.entrySet().stream().forEach(entry -> {
            DmDmRemovalReport removalReport = removalReportMap.get(entry.getKey());
            if (removalReport == null) {
                removalReport = new DmDmRemovalReport();
            }
            DmDmRemovalReport finalRemovalReport = removalReport;
            entry.getValue().values().stream().findFirst().ifPresent(demoVO -> {
                BeanConvertUtils
                        .toRemovalReportVOList(demoVO, finalRemovalReport, endDate)
                        .stream()
                        .forEach(x -> {
                            entry.getValue().put(x.getGroupCode(), x);
                        });
            });
        });
        LOCAL_CACHE.put("removal:base:" + endDate, new Object[]{dmDmValueReportList, buildingVOMap});
    }

	@Override
	public R getDivision(String apiVersion) {
		 List<String> divisionIdList = idmUserService.queryUserDivisionPermissions(getOperator().getUsername());
		 if (CollectionUtils.isEmpty(divisionIdList)){
	        	throw new MsgException(CodeEnum.DATA_PERMISSION_DENIED);
	     }
		 List<DivisionTreeBO> result = new ArrayList<>();
		 List<DivisionDto> list = orgService.getDivisionList(divisionIdList);
		 DivisionTreeBO bo = new DivisionTreeBO();
		 bo.setOrgId(Constants.XUHUI_GROUP);
		 bo.setOrgName(Constants.XUHUI_GROUP_NAME);
		 bo.setChildren(list);
		 result.add(bo);
		return ok(result);
	}
	
}
